package com.study.minio.utils;


import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * https://docs.min.io/docs/java-client-api-reference
 *
 * @author Mayjean
 * @date 2020/10/22
 */
@Slf4j
public class MinIoUtils {

    /**
     * @author Mayjean
     */
    public interface ProcessReturnObject {
        //    <T> T exec()
        Object execute() throws Exception;
    }

    private final String accessKey = "minioadmin";//""Q3AM3UQ867SPQQA43P2F";
    private final String secretKey = "minioadmin";//""zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG";
    private final String endpoint = "http://192.168.200.93:9000";//https://play.min.io";

    public MinIoUtils(){
        Logger.getLogger(OkHttpClient.class.getName()).setLevel(Level.FINE);
    }

    private MinioClient getMinioClient() {
        return MinioClient.builder().credentials(accessKey, secretKey)
                .endpoint(endpoint)
                .build();
    }

    public List<Bucket> listBuckets() {
        Object obj = process(() -> getMinioClient().listBuckets());
        return (List<Bucket>) obj;
    }

    public boolean bucketExists(final String bucketName) {
        if (Objects.isNull(bucketName)) {
            return false;
        }
        return (Boolean) process(() -> {
            BucketExistsArgs args = BucketExistsArgs.builder().bucket(bucketName).build();
            return getMinioClient().bucketExists(args);
        });
    }

    public boolean makeBucket(final String bucketName) {
        if (Objects.isNull(bucketName)) {
            return false;
        }
        process(() -> {
            MakeBucketArgs args = MakeBucketArgs.builder().bucket(bucketName).build();
            getMinioClient().makeBucket(args);
            return true;
        });
        return true;
    }

    /**
     * 给一个存储桶+对象前缀设置策略
     * <p>
     * {
     * "Statement": [
     * {
     * "Action": [
     * "s3:GetBucketLocation",
     * "s3:ListBucket"
     * ],
     * "Effect": "Allow",
     * "Principal": "*",
     * "Resource": "arn:aws:s3:::my-bucketname"
     * },
     * {
     * "Action": "s3:GetObject",
     * "Effect": "Allow",
     * "Principal": "*",
     * "Resource": "arn:aws:s3:::my-bucketname/myobject*"
     * }
     * ],
     * "Version": "2012-10-17"
     * }
     *
     * @param bucketName
     * @param policyJson
     * @return
     */
    public boolean setBucketPolicy(String bucketName, String policyJson) {
        if (Objects.isNull(bucketName) || Objects.isNull(policyJson)) {
            return false;
        }
        process(() -> {
            SetBucketPolicyArgs args = SetBucketPolicyArgs.builder()
                    .bucket(bucketName)
                    .config(policyJson)
                    .build();
            getMinioClient().setBucketPolicy(args);
            return true;
        });
        return true;
    }

    /**
     * 获得指定对象前缀的存储桶策略
     *
     * @param bucketName
     * @return
     */
    public String getBucketPolicy(String bucketName) {
        Object obj = process(() -> {
            String config = getMinioClient().getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());
            return config;
        });
        return (String) obj;
    }


    public boolean removeBucket(String bucketName) {
        process(() -> {
            getMinioClient().removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            return true;
        });
        return true;
    }


    /**
     * Upload known sized input stream.
     *
     * @param bucketName
     * @param objectName
     * @param inputStream new ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
     * @param contentType video/mp4
     * @return
     */
    public ObjectWriteResponse putObject(String bucketName, String objectName, InputStream inputStream, String contentType) {
        if(Objects.isNull(inputStream)){
            log.error("数据为空");
            return null;
        }
        Object obj = process(() -> {

            PutObjectArgs.Builder builder = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
//                   .stream(inputStream, objectSize, partSize)
                    .stream(inputStream, inputStream.available(), -1);

            if (Objects.nonNull(contentType)) {
                builder.contentType(contentType);
            }

            MinioClient minioClient = getMinioClient();
            if(Objects.isNull(minioClient)){
                log.error("minioClient为空");
                return null;
            }

            ObjectWriteResponse response = minioClient.putObject(builder.build());
            //log.info("versionId={},etag={}", response.versionId(), response.etag());
            return response;
        });
        return (ObjectWriteResponse) obj;
    }

    public ObjectWriteResponse putObject(String bucketName, String objectName, InputStream inputStream) {
        return putObject(bucketName, objectName, inputStream, null);
    }


    /**
     * get object given the bucket and object name
     *
     * @param bucketName
     * @param objectName
     * @return
     */
    public InputStream getObject(String bucketName, String objectName) {
        Object obj = process(() -> {
            InputStream stream = getMinioClient().getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
            // Read data from stream
            return stream;
        });
        return (InputStream) obj;
    }

    public StatObjectResponse statObject(String bucketName, String objectName) {
        Object obj = process(() -> {
            return getMinioClient().statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
        });
        return (StatObjectResponse) obj;
    }


    public String getPresignedObjectUrl(String bucketName, String objectName, int seconds) {
        return getPresignedObjectUrl(bucketName, objectName, seconds, null);
    }
    /**
     * Get presigned URL of an object for HTTP method, expiry time and custom request parameters.
     * 生成一个给HTTP GET请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行下载，即使其所在的存储桶是私有的。
     * 这个presigned URL可以设置一个失效时间，默认值是7天。
     *
     * @param bucketName
     * @param objectName
     * @return
     */
    public String getPresignedObjectUrl(String bucketName, String objectName, int seconds, Method method) {
        Object url = process(() -> {
            return getMinioClient().getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            //Method.DELETE
                            .method(Objects.isNull(method) ? Method.GET : method)
                            .bucket(bucketName)
                            .object(objectName)
                            .expiry(seconds)
                            .build());
        });

        return (String) url;
    }


    /**
     * Remove object
     *
     * @param bucketName
     * @param objectName
     * @return
     */
    public boolean removeObject(String bucketName, String objectName) {
        process(() -> {
            getMinioClient().removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
            return true;
        });
        return true;
    }


    private Object process(ProcessReturnObject process) {
        try {
            return process.execute();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 公开策略
     * @param bucketName
     * @return
     */
    public String publicPolicy(String bucketName){
        String policy =
                "{"
                        +"\"Statement\": ["
                        +"{"
                        +"\"Action\": ["
                        +"\"s3:GetBucketLocation\""
                        +",\"s3:ListBucket\""
                        +"],"
                        +"\"Effect\": \"Allow\","
                        +"\"Principal\": \"*\","
                        +"\"Resource\": \"arn:aws:s3:::"+bucketName+"\""
                        +"}"
                        +",{"
                        +"\"Action\": \"s3:GetObject\","
                        +"\"Effect\": \"Allow\","
                        +"\"Principal\": \"*\","
                        +"\"Resource\": \"arn:aws:s3:::"+bucketName+"/*\""
                        +"}"
                        +"],"
                        +"\"Version\": \"2012-10-17\""
                        +"}";

        return policy;
    }

}